package com.github.operationlog.mybatis;

import com.github.operationlog.OperationLogMethodInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;

/**
 * {@link MapperFactoryBean}的代理{@link MethodInterceptor}
 */
@Slf4j
public class MapperMethodInterceptor implements MethodInterceptor {

    private MapperFactoryBean mapperFactoryBean;

    private OperationLogMethodInterceptor operationLogMethodInterceptor;

    private Object mapperInstance;

    public MapperMethodInterceptor(MapperFactoryBean mapperFactoryBean,
                                   OperationLogMethodInterceptor operationLogMethodInterceptor) {
        this.mapperFactoryBean = mapperFactoryBean;
        this.operationLogMethodInterceptor = operationLogMethodInterceptor;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
        if (!"getObject".equals(method.getName())) {
            return ReflectionUtils.invokeMethod(method, this.mapperFactoryBean, args);
        }

        if (mapperFactoryBean.isSingleton() && mapperInstance != null) {
            return mapperInstance;
        }

        synchronized (this) {
            if (mapperFactoryBean.isSingleton() && mapperInstance != null) {
                return mapperInstance;
            } else {
                return createProxy();
            }
        }
    }

    private Object createProxy() {
        Object object;
        try {
            object = mapperFactoryBean.getObject();
        } catch (Exception ex) {
            throw new BeanCreationException(mapperFactoryBean.getMapperInterface().getName(),
                    "FactoryBean threw exception on object creation", ex);
        }

        Enhancer enhancer = new Enhancer();
        enhancer.setInterfaces(new Class[]{mapperFactoryBean.getMapperInterface()});

        if (!mapperFactoryBean.isSingleton()) {
            // 非单例，每次创建一个新的OperationLogMethodInterceptor
            OperationLogMethodInterceptor cloneOperationLogMethodInterceptor = operationLogMethodInterceptor.clone();
            cloneOperationLogMethodInterceptor.setTarget(object);
            operationLogMethodInterceptor.afterPropertiesSet();

            enhancer.setCallback(cloneOperationLogMethodInterceptor);
        } else {
            if (operationLogMethodInterceptor.getTarget() == null) {
                operationLogMethodInterceptor.setTarget(object);
                operationLogMethodInterceptor.afterPropertiesSet();
            }
            enhancer.setCallback(operationLogMethodInterceptor);
        }

        Object mapperProxy = enhancer.create();
        if (mapperFactoryBean.isSingleton() && mapperInstance == null) {
            mapperInstance = mapperProxy;
        }
        return mapperProxy;
    }
}
